17.15 Der Lebenszyklus eines Servlets 

Der Container für Servlets registriert eine Anfrage durch den Client und lädt das Servlet in den Speicher. Da Servlets normale Klassen sind, übernimmt ein spezieller Klassenlader diese Aufgabe. Die Abarbeitung findet anschließend in einem Thread statt, der die Methoden des Servlet-Objekts aufruft.
Wir wollen nun verfolgen, wie der Container die Arbeit an das Servlet delegiert. Über die Schnittstelle Servlet werden drei elementare Methoden für die Initialisierung, die Abarbeitung der Anfragen und die Beendigung vorgeschrieben. Der Ablauf dieser Methoden heißt Lebenszyklus eines Servlets.
Die folgende Aufzählung zeigt alle Methoden, die die Schnittstelle Servlet für alle Java-Servlets vorschreibt.
interface javax.servlet.Servlet |
- void service( ServletRequest req, ServletResponse res ) Der Container leitet die Anfrage an das Servlet an diese Stelle.
- ServletConfig getServletConfig() Liefert ein ServletConfig-Objekt, das Initialisierungs- und Startparameter kapselt.
- String getServletInfo() Liefert Informationen über das Servlet wie Autor, Version und Copyright.
Beispiel Ein Servlet implementiert getServletInfo(), um Informationen an den Servlet-Container zu übermitteln.
public class FirstServlet extends GenericServlet { public void service( ServletRequest request, ServletResponse response ) { } public String getServletInfo() { return "Ich bin der erste Erguss seiner Servlet-Fähigkeiten"; } } |
17.15.1 Abfragen bei service() 

In unserem ersten Beispiel haben wir uns der Klasse GenericServlet bedient, um eine service()-Methode zu überschreiben, die auf beliebige Anfragen des Clients antwortet. In der Regel wollen wir bei unterschiedlichen Anfragen aber auch unterschiedlich reagieren. Eine Anfrage ist zum Beispiel GET. Diese Form wird dann benutzt, wenn im Browser vom Benutzer eine URL eingetragen oder ein Verweis verfolgt wird. Neben GET-Anfragen gibt es noch POST-Anfragen, die für Formulare Verwendung finden.
Klasse HttpServlet
Damit wir auf GET-Anfragen anders reagieren können als auf POST-Anfragen, sind zwei Möglichkeiten denkbar: Entweder können wir die service()-Methode überschreiben, den Typ herausfinden und dann die Anfrage behandeln, oder wir verwenden eine andere Klasse, nämlich HttpServlet. Sie implementiert service() so, dass Anfragen an Methoden wie doGet(), doPost() und entsprechende Methoden weitergeleitet werden. Bei eigenen Anfragen wollen wir im Folgenden immer HttpServlet erweitern und service() nicht mehr direkt verwenden, sondern die entsprechenden doXXX()-Methoden.
Implementierung von service() in HttpServlet
Die service()-Methode der Klasse HttpServlet erfragt die verwendete Methode (GET oder POST) mit der Dienstmethode getMethod() vom aktuellen Request. Kurz skizziert hat sie folgendes Format:
protected void service( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { String method = req.getMethod (); if (method.equals(METHOD_GET)) { ... } else if (method.equals (METHOD_HEAD)) { ... } else if (method.equals (METHOD_POST)) { doPost (req, resp); } else { ... resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
Auf diese Weise testet service() zusätzlich auf METHOD_PUT, METHOD_DELETE, METHOD_OPTIONS und METHOD_TRACE.
17.15.2 Mehrere Anfragen beim Servlet und die Thread-Sicherheit 

In der Regel nutzt der Container pro Anfrage einen Thread, der dann die service()-Methode des Servlet-Objekts betritt und die Anfrage bearbeitet. Es gibt demnach für mehrere Aufträge keine unterschiedlichen Exemplare des Servlets, sondern lediglich unterschiedliche Threads bei einem Servlet-Exemplar. Aus diesem Grund ist zu bedenken, dass die Dienste seiteneffektfrei sein müssen. Es ist unsere Aufgabe, die Methode soweit zu synchronisieren, dass es keine negativen Auswirkungen der Parallelität gibt. Die Synchronisation wirkt sich natürlich auf die Ausführungsgeschwindigkeit nachteilig aus, so dass auf die passende Granularität zu achten ist.
17.15.3 Das Ende eines Servlets 

Wenn eine Servlet-Klasse nicht mehr benötigt wird und aus dem Speicher entfernt werden soll, ruft der Container zum Abschluss die Methode destroy() auf. Hier findet sich der Programmcode, der für die Freigabe sorgt und die aus init() bereitgestellten Ressourcen wieder freigibt. Häufig findet sich in init() und destroy() ein Mechanismus, der serialisierte Daten liest und schreibt. Doch was ist, wenn der Container vor destroy() den Geist aufgibt? Dies führt zu Problemen, wenn in destroy() Daten für die Persistenz serialisiert werden. Sie würden bei einem Absturz verloren gehen.



